/**
 * This file is part of Sonedyan.
 * 
 * Sonedyan is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation;
 * either version 3 of the License, or (at your option) any
 * later version.
 *
 * Sonedyan is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public
 * License along with Octave; see the file COPYING.  If not
 * see <http://www.gnu.org/licenses/>.
 * 
 * Copyright (C) 2009-2012 Jimmy Dubuisson <jimmy.dubuisson@gmail.com>
 */

package org.unige.mpej.eckmann.sonedyan.enron.bean;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Enumeration;

import javax.mail.Address;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import com.sun.mail.dsn.MessageHeaders;
import org.apache.log4j.Logger;

/**
 * email message parser
 */
public class EnronMessage 
{
	private static Logger log = Logger.getLogger(org.unige.mpej.eckmann.sonedyan.enron.bean.EnronMessage.class);
	
	private MimeBodyPart mimeBodyPart;
	private MessageHeaders messageHeaders;
	
	private String relativeFilePath;
	
	private String id;
	private Date sentDate;
	
	// we assume there is a unique sender
	private Address from;
	
	private Address[] toRecipients;
	private Address[] ccRecipients;
	private Address[] bccRecipients;
	
	private int numberOfToRecipients;
	private int numberOfCcRecipients;
	private int numberOfBccRecipients;
	
	// message subject
	private String subject;
	
	/**
	 * default constructor
	 */
	public EnronMessage(){}
	
	/**
	 * class constructor
	 */
	public EnronMessage(String baseFolderAbsolutePath, String fileAbsolutePath)
	{
		InputStream in = null;
		
		try
		{
			this.relativeFilePath = fileAbsolutePath.substring(new File(baseFolderAbsolutePath).getAbsolutePath().length());

			in = new BufferedInputStream(new FileInputStream(new File(fileAbsolutePath)));
			
			this.mimeBodyPart = new MimeBodyPart(in);
			this.messageHeaders = new MessageHeaders();
			
			Enumeration<Header> e = this.mimeBodyPart.getAllHeaders();
			
			while (e.hasMoreElements())
			{
				Header header = e.nextElement();
				
				this.messageHeaders.addHeader(header.getName(), header.getValue());
			}
			
			this.id = this.messageHeaders.getMessageID();
			this.sentDate = this.messageHeaders.getSentDate();
			// we assume that there is only one sender
			this.from = this.messageHeaders.getFrom()[0];
			
			this.toRecipients = this.messageHeaders.getRecipients(Message.RecipientType.TO);
			this.ccRecipients = this.messageHeaders.getRecipients(Message.RecipientType.CC);
			this.bccRecipients = this.messageHeaders.getRecipients(Message.RecipientType.BCC);
			
			this.subject = this.messageHeaders.getSubject();
			
			if (this.toRecipients != null){ this.numberOfToRecipients = this.toRecipients.length; }
			else { this.numberOfToRecipients = 0; }
			
			if (this.ccRecipients != null){ this.numberOfCcRecipients = this.ccRecipients.length; }
			else { this.numberOfCcRecipients = 0; }
			
			if (this.bccRecipients != null){ this.numberOfBccRecipients = this.bccRecipients.length; }
			else { this.numberOfBccRecipients = 0; }
			
			in.close();
		}
		catch(IOException e)
		{
			log.error("Unable to open file '" + fileAbsolutePath + "': " + e.getMessage());
		}
		catch(MessagingException e)
		{
			log.error("Unable to read message ('" + fileAbsolutePath + "'): " + e.getMessage());
		}
		finally
		{
			try
			{
				if (in != null)
				{
					in.close();
				}
			}
			catch(Exception e){}
		}
		
	}
	
	/**
	 * get addresse as string
	 */
	static public String getAddressAsString(Address address)
	{
		try
		{
			if (address != null)
			{
				return address.toString();
			}
			else
			{
				return null;
			}
		}
		catch(Exception e)
		{
			log.error("An error occured: " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get addresses as a comma separated string
	 */
	public static String getAddressesAsString(Address[] addresses)
	{
		try
		{
			if (addresses != null)
			{
				StringBuffer buffer = new StringBuffer();
			
				for (int i = 0; i < addresses.length - 1; i++)
				{
					buffer.append(addresses[i].toString() + ",");
				}
				
				buffer.append(addresses[addresses.length - 1]);
				
				return buffer.toString();
			}
			else
			{
				return null;
			}
		}
		catch(Exception e)
		{
			log.error("An error occured: " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get headers as string
	 */
	public String getHeadersAsString()
	{
		try
		{
			return (String) this.messageHeaders.getContent();
		}
		catch(Exception e)
		{
			log.error("Unable to get headers as string: " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get body as string
	 */
	public String getMimeBodyAsString()
	{
		try
		{
			return (String) this.mimeBodyPart.getContent();
		}
		catch(Exception e)
		{
			log.error("Unable to get body as string: " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get the relative message file path
	 */
	public String getRelativeFilePath()
	{
		return this.relativeFilePath;
	}
	
	/**
	 * get message encoding
	 */
	public String getEncoding()
	{
		try
		{
			return this.mimeBodyPart.getEncoding();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message encoding (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get message content type
	 */
	public String getContentType()
	{
		try
		{
			return this.mimeBodyPart.getContentType();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message content type (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get message size
	 */
	public int getSize()
	{
		try
		{
			return this.mimeBodyPart.getSize();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message size (ID '" + this.id + "'): " + e.getMessage());
			return 0;
		}
	}
	
	/**
	 * get message Id
	 */
	public String getId()
	{
		return id;
	}
	
	/**
	 * get message subject
	 */
	public String getSubject()
	{
		return subject;
	}
	
	/**
	 * get message sent date
	 */
	public Date getSentDate()
	{
		return sentDate;
	}
	
	/**
	 * get message from
	 */
	public Address getFrom()
	{
		return from;
	}
	
	/**
	 * get message sender
	 */
	public Address getSender()
	{
		try
		{
			return this.messageHeaders.getSender();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message sender (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get message replyTo
	 */
	public Address[] getReplyTo()
	{
		try
		{
			return this.messageHeaders.getReplyTo();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message replyTo (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get message recipients
	 * 
	 * NB: type can be Message.RecipientType.TO, Message.RecipientType.CC, Message.RecipientType.BCC
	 */
	public Address[] getRecipients(Message.RecipientType type)
	{
		try
		{
			return this.messageHeaders.getRecipients(type);
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message recipients (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	/**
	 * get message TO recipients
	 */
	public Address[] getToRecipients()
	{
		return toRecipients;
	}
	
	/**
	 * get message CC recipients
	 */
	public Address[] getCcRecipients()
	{
		return ccRecipients;
	}
	
	/**
	 * get message BCC recipients
	 */
	public Address[] getBccRecipients()
	{
		return bccRecipients;
	}
	
	/**
	 * get message content
	 */
	public Object getContent()
	{
		try
		{
			return this.mimeBodyPart.getContent();
		}
		catch(MessagingException e)
		{
			log.error("Unable to get message content (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
		catch(IOException e)
		{
			log.error("Unable to get message content (ID '" + this.id + "'): " + e.getMessage());
			return null;
		}
	}
	
	public MimeBodyPart getMimeBodyPart()
	{
		return mimeBodyPart;
	}

	public void setMimeBodyPart(MimeBodyPart mimeBodyPart)
	{
		this.mimeBodyPart = mimeBodyPart;
	}
	
	public void setMimeBodyPart(String content)
	{
		try
		{
			if (content != null)
			{
				this.mimeBodyPart = new MimeBodyPart(new BufferedInputStream(new ByteArrayInputStream(content.getBytes())));
				this.messageHeaders = new MessageHeaders();
				
				Enumeration<Header> e = this.mimeBodyPart.getAllHeaders();
				
				while (e.hasMoreElements())
				{
					Header header = e.nextElement();
					
					this.messageHeaders.addHeader(header.getName(), header.getValue());
				}
			}
			else
			{
				log.info("Provided content is null");
			}
		}
		catch(Exception e)
		{
			log.error("An error occured while parsing message content: " + e.getMessage());
		}
	}

	public MessageHeaders getMessageHeaders()
	{
		return messageHeaders;
	}

	public void setMessageHeaders(MessageHeaders messageHeaders)
	{
		this.messageHeaders = messageHeaders;
	}
	
	public void setRelativeFilePath(String relativeFilePath)
	{
		this.relativeFilePath = relativeFilePath;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public void setSentDate(Date sentDate)
	{
		this.sentDate = sentDate;
	}

	public void setFrom(Address from)
	{
		this.from = from;
	}

	public void setToRecipients(Address[] toRecipients)
	{
		this.toRecipients = toRecipients;
	}

	public void setCcRecipients(Address[] ccRecipients)
	{
		this.ccRecipients = ccRecipients;
	}

	public void setBccRecipients(Address[] bccRecipients)
	{
		this.bccRecipients = bccRecipients;
	}
	
	public void setSubject(String subject)
	{
		this.subject = subject;
	}
	
	public int getNumberOfToRecipients()
	{
		return numberOfToRecipients;
	}

	public void setNumberOfToRecipients(int numberOfToRecipients)
	{
		this.numberOfToRecipients = numberOfToRecipients;
	}

	public int getNumberOfCcRecipients()
	{
		return numberOfCcRecipients;
	}

	public void setNumberOfCcRecipients(int numberOfCcRecipients)
	{
		this.numberOfCcRecipients = numberOfCcRecipients;
	}

	public int getNumberOfBccRecipients()
	{
		return numberOfBccRecipients;
	}

	public void setNumberOfBccRecipients(int numberOfBccRecipients)
	{
		this.numberOfBccRecipients = numberOfBccRecipients;
	}
	
	/**
	 * main method
	 */
	public static void main(String args[])
	{
		InputStream in = null;
		
		try
		{
			String fileName = args[0];
			in = new BufferedInputStream(new FileInputStream(new File(fileName)));
			
			System.out.println("========== MimeBodyPart ==========\n\n");
			
			MimeBodyPart mimeBodyPart = new MimeBodyPart(in);
			
			System.out.println("Encoding: " + mimeBodyPart.getEncoding());
			System.out.println("Content ID: " + mimeBodyPart.getContentID());
			System.out.println("Content type: " + mimeBodyPart.getContentType());
			System.out.println("Content: " + mimeBodyPart.getContent());
			System.out.println("Size: " + mimeBodyPart.getSize());
			
			Enumeration<Header> e = mimeBodyPart.getAllHeaders();
			
			MessageHeaders messageHeaders = new MessageHeaders();
			
			while (e.hasMoreElements())
			{
				Header header = e.nextElement();
				
				messageHeaders.addHeader(header.getName(), header.getValue());
				System.out.println(header.getName() + ": " + header.getValue() + "\n");
			}
			
			System.out.println("========== MessageHeaders ==========\n\n");
			
			System.out.println("ID: " + messageHeaders.getMessageID());
			System.out.println("Number: " + messageHeaders.getMessageNumber());
			System.out.println("Subject: " + messageHeaders.getSubject());
			System.out.println("Sent date: " + messageHeaders.getSentDate());
			System.out.println("Received date: " + messageHeaders.getReceivedDate());
			System.out.println("From: " + messageHeaders.getFrom());
			System.out.println("Sender: " + messageHeaders.getSender());
			System.out.println("Reply to: " + messageHeaders.getReplyTo());
			System.out.println("Recipients: " + messageHeaders.getRecipients(Message.RecipientType.TO));
			System.out.println("Folder: " + messageHeaders.getFolder());
			
			in.close();
		}
		catch(Exception e)
		{
			System.err.println("An error occured: " + e.getMessage());
		}
		finally
		{
			try
			{
				if (in != null)
				{
					in.close();
				}
			}
			catch(Exception e){}
		}
	}
	
}
